package com.websense.training.books

import java.lang.{ Object => JavaObject }
import java.io.FileOutputStream
import java.io.IOException
import java.sql.SQLException
import java.util.{ ArrayList => JavaArrayList }
import java.util.{ HashMap => JavaHashMap }
import java.util.{ List => JavaList }
import com.itextpdf.text.Document
import com.itextpdf.text.DocumentException
import com.itextpdf.text.pdf.PdfCopy
import com.itextpdf.text.pdf.PdfReader
import com.itextpdf.text.pdf.SimpleBookmark
import com.itextpdf.text.pdf.PdfWriter
import com.itextpdf.text.pdf.PdfDestination
import com.itextpdf.text.pdf.PdfStamper
import scala.xml.XML
import com.itextpdf.text.pdf.PdfContentByte
import com.itextpdf.text.pdf.PdfOutline
import com.itextpdf.text.pdf.PdfAction
import com.itextpdf.text.Chunk
import com.itextpdf.text.pdf.ColumnText
import com.itextpdf.text.Element
import com.itextpdf.text.Phrase
import com.itextpdf.text.FontFactory
import com.itextpdf.text.Font
import com.itextpdf.text.BaseColor

object BuildBookPdf {

  val PAGE_NUM_FONT = 9
  var pdfInFile = ""
  var pdfOutFile = ""
  def makeCover(title: String, subtitle: String, revision: String): Unit = {

    val staticData = Map("title" -> title,
      "booktype" -> subtitle,
      "revision" -> revision)
    val staticParams = Map("title" -> List(1, 38, 496, 15),
      "booktype" -> List(1, 38, 476, 15),
      "revision" -> List(1, 38, 458, 11))
    val buildStamper = new BuildStamper(pdfInFile, pdfOutFile)
    buildStamper.processStatic(staticData, staticParams);
  }

  def countPages(src: Array[String]): Array[Int] = {
    for (ff <- src) yield {
      val reader = new PdfReader(ff)
      reader.consolidateNamedDestinations()
      reader.getNumberOfPages
    }
  }

  /**
   * Concatenates array of PDF files 'src' with the file 'dest' as result.
   * Tries to preserve bookmarks by merging them.
   */
  def manipulatePdf(src: Array[String], dest: String, marks: List[String]): Unit = {
    var document: Document = new Document()
    
    var copy: PdfCopy = new PdfCopy(document, new FileOutputStream(dest))

    document.open()

    // attempt to make new bookmarks
    val cb: PdfContentByte = copy.getDirectContent()
    val rootOutline: PdfOutline = cb.getRootOutline()

    val oline1: PdfOutline = new PdfOutline(rootOutline,
      PdfAction.gotoLocalPage("1", false), "Cover")
    val oline2: PdfOutline = new PdfOutline(oline1,
      PdfAction.gotoLocalPage("3", false), "Copyright")
    val oline3: PdfOutline = new PdfOutline(oline1,
      PdfAction.gotoLocalPage("5", false), "Table of Contents")
    for (markIdx <- 0 until marks.length) {
      val oline1: PdfOutline = new PdfOutline(rootOutline,
        PdfAction.gotoLocalPage("" + (6 + markIdx), false), marks(markIdx))
    }

    var reader: PdfReader = null
    var page_offset: Int = 0
    var n: Int = 0
    // Create a list for the bookmarks
    val bookmarks: JavaArrayList[JavaHashMap[String, Object]] = new JavaArrayList
    var tmp: JavaList[JavaHashMap[String, Object]] = new JavaArrayList
    var globalPageCount = 0
    for (i <- 0 until src.length) {
      reader = new PdfReader(src(i))
      reader.consolidateNamedDestinations()
      n = reader.getNumberOfPages
      Console.out.println("numPages is " + n)
      for (page <- 0 until n) {
        globalPageCount = globalPageCount + 1
        val pdfImpPage = copy.getImportedPage(reader, page + 1)
        if (page == 0) {
          val stamp: PdfCopy.PageStamp = copy.createPageStamp(pdfImpPage)
          val chunk00 = new Chunk("" + (i + 1),
            FontFactory
              .getFont(FontFactory.HELVETICA, 6,
                Font.NORMAL, BaseColor.WHITE))
          chunk00.setLocalDestination("" + (i + 1))
          val phrase00 = new Phrase(chunk00)
          ColumnText.showTextAligned(
            stamp.getUnderContent(), Element.ALIGN_LEFT,
            phrase00,
            36.0f, 792.0f, 0);
          stamp.alterContents();
        }

        if (globalPageCount > 4) {
          val stamp: PdfCopy.PageStamp = copy.createPageStamp(pdfImpPage)
          val pb2 = stamp.getOverContent()

          pb2.saveState()
          pb2.setColorStroke(BaseColor.WHITE)
          pb2.setColorFill(BaseColor.WHITE)

          val EvenX0 = 72f
          val EvenY0 = 36f
          val OddX0 = 340f
          val OddY0 = 36f
          val RectHeight = 20f
          val RectWidth = 200f
          if (globalPageCount % 2 == 0) {
            pb2.moveTo(EvenX0, EvenY0)
            pb2.lineTo(EvenX0, EvenY0 + RectHeight)
            pb2.lineTo(EvenX0 + RectWidth, EvenY0 + RectHeight)
            pb2.lineTo(EvenX0 + RectWidth, EvenY0)
          } else {
            pb2.moveTo(OddX0, OddY0)
            pb2.lineTo(OddX0, OddY0 + RectHeight)
            pb2.lineTo(OddX0 + RectWidth, OddY0 + RectHeight)
            pb2.lineTo(OddX0 + RectWidth, OddY0)
          }
          pb2.closePathFillStroke()
          pb2.restoreState()

          val stampText = if (globalPageCount % 2 == 0) {
            "" + globalPageCount + "\u00A0\u00A0\u00A0\u00A0\u00A0DSS v7.8 Professional"
          } else {
            "DSS v7.8 Professional\u00A0\u00A0\u00A0\u00A0\u00A0" + globalPageCount
          }

          val chunk00 = new Chunk(stampText,
            FontFactory
              .getFont(FontFactory.HELVETICA, PAGE_NUM_FONT,
                Font.NORMAL, BaseColor.BLACK))
          //println("page is " + globalPageCount + " width is " + chunk00.getWidthPoint())
          val phrase00 = new Phrase(chunk00)

          if (globalPageCount % 2 == 0) {
            ColumnText.showTextAligned(
              stamp.getOverContent(), Element.ALIGN_LEFT,
              phrase00,
              91.0f, 41.0f, 0)
          } else {
            ColumnText.showTextAligned(
              stamp.getOverContent(), Element.ALIGN_RIGHT,
              phrase00,
              521.0f, 41.0f, 0)
          }
          //val pb2 = stamp.getOverContent()
          pb2.saveState()
          pb2.setColorStroke(new BaseColor(12, 33, 115))
          pb2.setColorFill(new BaseColor(12, 33, 115))

          val TriangleSide = 6f
          val TriangleHeight = 3 * Math.sqrt(3).toFloat
          val numWidth = new Chunk("" + globalPageCount,
            FontFactory
              .getFont(FontFactory.HELVETICA, PAGE_NUM_FONT,
                Font.NORMAL, BaseColor.BLACK)).getWidthPoint()
          val RightX0 = 512f
          val RightY0 = 41f
          val LeftX0 = 101f
          val LeftY0 = 41f

          if (globalPageCount % 2 == 0) {
            pb2.moveTo(LeftX0 + numWidth, LeftY0)
            pb2.lineTo(LeftX0 + numWidth, LeftY0 + TriangleSide)
            pb2.lineTo(LeftX0 + numWidth - TriangleHeight, LeftY0 + TriangleSide / 2)
          } else {
            pb2.moveTo(RightX0 - numWidth, RightY0)
            pb2.lineTo(RightX0 - numWidth, RightY0 + TriangleSide)
            pb2.lineTo(RightX0 - numWidth + TriangleHeight, RightY0 + TriangleSide / 2)
          }
          pb2.closePathFillStroke()
          pb2.restoreState()

          stamp.alterContents()
        }
        copy.addPage(pdfImpPage)
      }
      copy.freeReader(reader)
      tmp = SimpleBookmark.getBookmark(reader)

      if (tmp != null) {
        //        SimpleBookmark.exportToXML(tmp, new FileOutputStream("resources/T"+i+".xml"), "UTF-8", true)
        Console.out.println("tmp.size is " + tmp.size())
        for (i <- 0 until tmp.size()) {
          Console.out.println("   " + tmp.get(i))
        }

        SimpleBookmark.shiftPageNumbers(tmp, page_offset, null)
        bookmarks.addAll(tmp);
      }
      page_offset += n;
    }
    // set the concatenated bookmarks
    //    copy.setOutlines(bookmarks);
    // by default opens the bookmarks in the left navbar
    copy.setViewerPreferences(PdfWriter.PageModeUseOutlines)
    document.close();

    val destReader: PdfReader = new PdfReader(dest)
    destReader.makeRemoteNamedDestinationsLocal()
    val stamper: PdfStamper = new PdfStamper(destReader, new FileOutputStream(dest + ".pdf"))
    stamper.close()
  }

  def main(args: Array[String]): Unit = {
    val data = XML.loadFile(args(0));

    val baseDir = (data \\ "book")(0).attribute("basedir").get(0).text
    val chapterDir = (data \\ "chapters")(0).attribute("basedir").get(0).text
    println("baseDir = " + baseDir)

    val title = (data \\ "cover" \\ "title").head.text
    val subtitle = (data \\ "cover" \\ "subtitle").head.text
    val revision = BuildDateUtil.getCode((data \\ "cover" \\ "date").head.text)

    pdfInFile = baseDir + "empty-cover.pdf"
    pdfOutFile = baseDir + "my-cover.pdf"
    makeCover(title, subtitle, revision)

    val copyRightLines = (data \\ "copyright" \\ "para").
      map(_.text.replaceAll("""(?m)\s+""", " "))

    val btp = new BuildTextParagraphs(null,
      "resources/copyright.pdf")
    btp.processStatic(copyRightLines.toList)

    val chapters = data \\ "chapter";
    val chTitles =
      for (
        ch <- chapters
      ) yield {
        ch.attribute("bookmark").get(0).text
      }

    val listPrefix = List(baseDir + "my-cover.pdf",
      baseDir + "empty-page.pdf",
      baseDir + "copyright.pdf",
      baseDir + "empty-page.pdf",
      baseDir + "toc_tmp.pdf")
    val chList =
      for (
        ch <- chapters
      ) yield {
        val refStr = ch.attribute("ref").get(0).text
        chapterDir + refStr + "/Frame/Edited-US/" + refStr + ".pdf"
      }
    val counts = countPages(chList.toArray)
    Console.out.println("Counts are " + counts.toList)
    val bToc = new BuildToc("resources/toc_tmp.pdf")
    bToc.processStatic(chTitles.toList, counts.toList)

    val bigList = listPrefix ++ chList
    val resultFile = (data \\ "book")(0).attribute("result").get.text
    manipulatePdf(bigList.toArray, baseDir + resultFile, chTitles.toList);
  }
}

